﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using System.Net;
using System.IO;

namespace ServerASISTSYS
{
    class Server
    {
        // Constants
        static readonly int BUF_SIZE        = 4096;        // maximum buffer size

        // Private members
        static private TcpListener tcpListener;            // handle to the client listener
        static private Thread      listenThread;           // handle to the client listener thread


        /// <summary>
        /// Main function. It is called when the program starts.
        /// </summary>
        /// <param name="args">Arguments string passed by the calling app</param>
        static void Main(string[] args)
        {
            // Parse program arguments (if necessary)

            // Create a new TCP Listener
            tcpListener = new TcpListener(IPAddress.Any, ServerConfig.GetInstance().GetPort());
            listenThread = new Thread(new ThreadStart(ClientListener));
            listenThread.Name = "ClientListener";
            listenThread.Start();
            Console.WriteLine("client listener thread ID: " + listenThread.ManagedThreadId);
        }


        /// <summary>
        /// Handles the listening procedure for new clients.
        /// It must run in a new Thread!
        /// </summary>
        static private void ClientListener()
        {
            try
            {
                tcpListener.Start();
                Console.WriteLine("client listener has started.");

                while (true)
                {
                    TcpClient client = tcpListener.AcceptTcpClient();
                    Thread clientThread = new Thread(new ParameterizedThreadStart(ClientHandler));
                    clientThread.Start(client);
                    Console.WriteLine("new client thread started: " + clientThread.ManagedThreadId);
                }
            }
            catch (System.Net.Sockets.SocketException)
            {
            }
            catch (System.Threading.ThreadStateException)
            {
            }
            catch (System.OutOfMemoryException)
            {
                Console.WriteLine("Too many threads! Out of Memory.");
            }

            Console.WriteLine("client listener has stoped.");
        }


        /// <summary>
        /// It handles the client commutication
        /// It must run in a new Thread!
        /// </summary>
        /// <param name="client">The new client</param>
        static private void ClientHandler(object client)
        {
            // TCP stuff...
            TcpClient tcpClient = (TcpClient)client;
            NetworkStream clientStream = tcpClient.GetStream();

            // Connection buffers...
            int bytesRead;
            byte[] rawData = new byte[BUF_SIZE];
            int msgLen;
            int msgID;

            // Data processing stuff...
            ServerFunctions sf = new ServerFunctions();
            FilterManager fm = new FilterManager(sf);
            fm.ScanAndLoad();

            while (true)
            {
                bytesRead = 0;

                try
                {
                    //blocks until a client sends a message
                    if ((bytesRead = clientStream.Read(rawData, 0, 8)) != 8)
                        throw new Exception();

                    // Get message length & ID
                    msgLen = BitConverter.ToInt32(rawData, 0);
                    msgID = BitConverter.ToInt32(rawData, 4);

                    // Get message body
                    if (msgLen > 0 &&
                         (bytesRead = clientStream.Read(rawData, 0, msgLen)) != msgLen)
                        throw new Exception();

                    if (msgID <= 100)
                    {
                        // data for server...
                        switch (msgID)
                        {
                            case 12: // CNP
                                for (int i = 0; i < msgLen; i++)
                                    sf.CNP += (char)rawData[i];
                                break;

                            case 14: // GUI
                                sf.AddGUIClient(tcpClient);
                                break;

                            case 16: // WebCam: broadcast the alert
                                foreach (TcpClient cl in ServerFunctions.tcpClientList)
                                {
                                    try
                                    {
                                        Stream stm = cl.GetStream();
                                        byte[] buff = new byte[4096], tmp;
                                        int j = 0;

                                        // length
                                        tmp = BitConverter.GetBytes(msgLen);
                                        for (int i = 0; i < 4; i++, j++)
                                            buff[j] = tmp[i];

                                        // now the msg
                                        for (int i = 0; i < msgLen; i++, j++)
                                            buff[j] = rawData[i];

                                        // send
                                        stm.Write(buff, 0, j);
                                    }
                                    catch// (Exception e)
                                    {
                                        // daca s-a produs o exceptie inchidem clientul si il stergem in lista
                                        cl.Close();
                                        ServerFunctions.tcpClientList.Remove(cl);   
                                        Console.WriteLine("EXCEPTION in Server: Client ?? has been closed.");
                                    }
                                }
                                break;

                            default:
                                // ??
                                break;
                        }
                    }
                    else
                    {
                        // data for sensor filter...
                        fm.ProcessMessage(msgLen, msgID, rawData);
                    }
                }
                catch (Exceptions.UnknownMessageID x)
                {
                    // the message could not be processed
                    Console.WriteLine("Unknown message ID(" +
                                      x.ID +
                                      "). The message will not be processed.");
                }
                catch (Exception x)
                {
                    //a socket error has occured
                    Console.WriteLine("Exception occured in ClientHandler thread " +
                                      Thread.CurrentThread.ManagedThreadId +
                                      " and it will be closed.");
                    Console.WriteLine("Reason: " +
                                      x.Message);
                    break;
                }
            }
        }


    } /* class Server */
} /* namespace ServerASISTSYS */
